using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;

//**********************************************************************
//NOTE:  CHANGED 11-11-08 BECAUSE MICROSOFT.SHAREPOINT.WEBPARTPAGES 
//CAUSED OTHER CUSTOM WEB PARTS BASED ON ASP.NET WEBPARTS TO
//THROW AN ERROR WHEN CASTING TO TYPE WEBPART DURING AN ADD OPERATION
//WOULD NEED TO ADD SOME MORE CODE TO FIGURE OUT WHICH CLASS A PART IS
//BASED ON AND DO THE APPROPRIATE CAST.  FOR NOW CHANGING THE USING
//STATEMENT SO IT WILL WORK WITH THE COMMON CASE CUSTOM WEB PARTS

//using Microsoft.SharePoint.WebPartPages;
using System.Web.UI.WebControls.WebParts;
//***********************************************************************

using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Diagnostics;
using Microsoft.SharePoint.Portal.WebControls;
using System.Collections;
using System.Reflection;
using System.ComponentModel;


namespace Microsoft.IW
{

	[XmlRoot("Microsoft.IW")]
	public class PartCheck : WebControl
	{

		//constants for property simple property setter value tag replacements
		public const string PSV_DOMAIN = "[DOMAIN]";
		public const string PSV_ACCOUNTNAME = "[ACCOUNTNAME]";
		public const string PSV_MACHINE = "[MACHINE]";
		public const string PSV_LOGIN = "[LOGIN]";
		

		protected override void OnLoad(EventArgs e)
		{
			//do the base load functions
			base.OnLoad(e);

			//look to see if our web has had this stuff run yet
			const string KEY_CHK = "Microsoft.IW.PartCheck";
			
			//generic dictionary to store the list of webparts we're going to work 
			//with and what we'll do with each; Steve likes generics!  :-)
			Dictionary<string, WebPartAction> wpList = null;

			//web part action class to store info about web parts that we're changing
			WebPartAction wpa = null;	

			//webpart class that will be used in the action class
			WebPart xWp = null;

			//assembly we'll use to load web parts we are adding
			Assembly wpAsm = null;

			//serializer to convert the xml file into objects
			XmlSerializer slz = null;

			try
			{
				//get the current web; not using "using" because we don't want to kill
				//the web context for other controls that need it
				SPWeb curWeb = SPContext.Current.Web;

				//look to see if our code has already run
				if (!curWeb.Properties.ContainsKey(KEY_CHK))
				{
					//turn on unsafe updates so we can make changes on a GET
					curWeb.AllowUnsafeUpdates = true;

					//look for the default page so we can mess with the web parts
					SPFile thePage = curWeb.RootFolder.Files["default.aspx"];

					if (thePage != null)
					{
						//look for the xml manifest of web part changes
						SPQuery qry = new SPQuery();
						qry.Query = "<Where><Eq><FieldRef Name='FileLeafRef'/><Value Type='Text'>" +
							"MySiteStaplee.xml</Value></Eq></Where>";

						//try opening the file
						SPList xList = curWeb.GetCatalog(SPListTemplateType.MasterPageCatalog);
						SPListItemCollection xItems = xList.GetItems(qry);

						if ((xItems != null) && (xItems.Count > 0))
							{
							//now that we have the file, read it into an xml document
							XmlDocument xDoc = new XmlDocument();
							xDoc.Load(xItems[0].File.OpenBinaryStream());
							
							//create a hashtable to store our web parts
							wpList = new Dictionary<string,WebPartAction>();
							
							//create a new serializer
							slz = new XmlSerializer(typeof(Microsoft.IW.WebPartAction));

							//enumerate through the child nodes
							foreach (XmlNode xNode in xDoc.FirstChild.ChildNodes)
							{

								try
								{
									//try serializing each one
									wpa = slz.Deserialize(new System.IO.StringReader(xNode.OuterXml)) as
										Microsoft.IW.WebPartAction;
								}
								catch (Exception srlzEx)
								{
									//some error logging here about invalid xml file
									UpdateLog("Error serializing MySiteStaplee.xml file: " + srlzEx.Message,
										EventLogEntryType.Error);

									//set our reference to null so we don't try and do something with
									//a version that was successfully serialized previously
									wpa = null;
								}

								//make sure we serialized, and also make sure this is an Add
								if (wpa != null)
								{
									switch (wpa.Action)
									{
										case WebPartAction.ActionType.Add:

											//now try loading the assembly
											try
											{
												wpAsm = Assembly.Load(wpa.assemblyName);
											}
											catch (Exception asmEx)
											{
												//some error logging here about invalid assembly name
												UpdateLog("Error loading assembly name " + wpa.assemblyName +
													": " + asmEx.Message, EventLogEntryType.Error);

												//set our reference to null so we don't try and do something with
												//a version that was successfully loaded previously
												wpAsm = null;
											}

											if (wpAsm != null)
											{
												//try creating an instance of the class
												try
												{
													xWp = (WebPart)wpAsm.CreateInstance(wpa.className);
												}
												catch (Exception instEx)
												{
													//some error logging here about invalid class name
													UpdateLog("Error creating instance of class " + wpa.className +
														": " + instEx.Message, EventLogEntryType.Error);

													//set our reference to null so we don't try and do something with
													//a version that was successfully loaded previously
													xWp = null;
												}

												//plug it into our class
												if (xWp != null)
												{
													//enumerate and add properties to part
													SetWebPartProperties(wpa, xWp);

													//add part to class
													wpa.wp = xWp;
												}
											}

											//add it to the hashtable
											wpList.Add(Guid.NewGuid().ToString(), wpa);
											break;
										default:
											//for delete, move or SetProperties we want to capture 
											//the type name and plug it into the hash array
											wpList.Add(wpa.typeName, wpa);
											break;
									}
								}
							}

							//NOTE:  CHANGED 11-11-08 BECAUSE TOOK MICROSOFT.SHAREPOINT.WEBPARTPAGES OUT OF
							//THE USING LIST BECAUSE OTHER CUSTOM WEB PARTS BASED ON ASP.NET WEBPARTS WOULD
							//THROW AN ERROR WHEN CASTING TO TYPE WEBPART
							//get the web part manager
							//SPLimitedWebPartManager theMan =
							//  thePage.GetLimitedWebPartManager
							//  (System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
							Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager theMan =
								thePage.GetLimitedWebPartManager
								(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

							foreach (WebPart wp in theMan.WebParts)
							{
								//check each web part to see if matches our typeName
								if (wpList.ContainsKey(wp.GetType().ToString()))
									wpList[wp.GetType().ToString()].wp = wp;
							}

							//now enumerate items in hash; can't do it in WebPart collection 
							//on SPLimitedWebPartManager or it fails
							foreach (string key in wpList.Keys)
							{
								wpa = wpList[key];

								switch (wpa.Action)
								{
									case WebPartAction.ActionType.Delete:
										theMan.DeleteWebPart(wpa.wp);
										break;
									case WebPartAction.ActionType.Move:
										theMan.MoveWebPart(wpa.wp, wpa.zoneID, int.Parse(wpa.zoneIndex));
										theMan.SaveChanges(wpa.wp);
										break;
									case WebPartAction.ActionType.Add:
										theMan.AddWebPart(wpa.wp, wpa.zoneID, int.Parse(wpa.zoneIndex));
										break;
									case WebPartAction.ActionType.SetProperties:
										SetWebPartProperties(wpa, wpa.wp);
										theMan.SaveChanges(wpa.wp);
										break;
								}
							}

							//add our key to the property bag so we don't run our provisioning code again
							curWeb.Properties.Add(KEY_CHK, "true");
							curWeb.Properties.Update();
							curWeb.AllowUnsafeUpdates = false;

							//force a page refresh to show the page with the updated layout
							Context.Response.Redirect(thePage.Url);

							//potentially delete our xml manifest file that tells us what to do
							//would be cool if we could just remove ourselves from the page
							//or maybe we stay there so we could be activated again in the future?? That
							//would be an interesting idea...
						}
					}
				}
			}
			catch (System.Threading.ThreadAbortException abortEx)
			{
				//we'll throw a thread abort exception on redirect, so we can ignore it, but
				//record everything else
			}
			catch (Exception ex)
			{
					UpdateLog("Error checking web parts: " + ex.Message, EventLogEntryType.Error);
			}
		}

		private void SetWebPartProperties(WebPartAction wpa, WebPart xWp)
		{
			//check to see if there are any properties; if there are zero it won't say
			//zero, it will say null (unlike vb.net)
			if (wpa.Properties == null)
				return;

			//enumerate and add properties to part
			for (int p = 0; p < wpa.Properties.Property.Length; p++)
			{
				try
				{
					xWp.GetType().GetProperty(wpa.Properties.Property[p].Key).SetValue(xWp,
						GetPropertySetterValue(wpa.Properties.Property[p].Value), null);
				}
				catch (Exception propEx)
				{
					//some error logging here about invalid property name
					UpdateLog("Error setting property value " +
						wpa.Properties.Property[p].Key + " to " +
						wpa.Properties.Property[p].Value +
						": " + propEx.Message, EventLogEntryType.Error);
				}
			}
		}

		private string GetPropertySetterValue(string Value)
		{
			string ret = Value;

			switch (Value)
			{
				case PSV_ACCOUNTNAME:
					ret = Environment.UserName;
					break;
				case PSV_DOMAIN:
					ret = Environment.UserDomainName;
					break;
				case PSV_MACHINE:
					ret = Environment.MachineName;
					break;
				case PSV_LOGIN:
					ret = Environment.UserDomainName + "\\" + Environment.UserName;
					break;
				default:
					break;
			}

			return ret;
		}

		private void UpdateLog(string Message, EventLogEntryType msgType)
		{
			try
			{
				System.Diagnostics.EventLog.WriteEntry("MySiteCreatePart", Message, msgType);
			}
			catch
			{
				//ignore
			}
		}
	}

	//Unused Stuff
						//  for (int i = 0; i < xList.Items[17].Fields.Count; i++)
						//{
						//  try
						//  {
						//    Debug.WriteLine(xList.Items[17].Fields[i].InternalName + " = " + xList.Items[17][i].ToString());
						//  }
						//  catch
						//  {
						//    //ignore
						//  }
						//}
						//for (int i = 0; i < xList.Items[17].Fields.Count; i++)
						//{
						//  try
						//  {
						//    Debug.WriteLine(xList.Items[17].Fields[i].InternalName + " = " + xList.Items[17][i].ToString());
						//  }
						//  catch
						//  {
						//    //ignore
						//  }
						//}
	////FYI for us, show the title and type of web part
	//Debug.WriteLine(wp.Title + " is type " + wp.GetType().ToString());

	////close the welcome web part
	//if (wp.GetType().Equals(typeof(PersonalWelcomeWebPart)))
	//  hshWp.Add(wp.StorageKey.ToString(),
	//    new WebPartAction(wp, WebPartAction.ActionType.Delete));

	////move the RSS web part to the bottom
	//if (wp.GetType().Equals(typeof(RSSAggregatorWebPart)))
	//  hshWp.Add(wp.StorageKey.ToString(),
	//    new WebPartAction(wp, WebPartAction.ActionType.Move, "BottomZone", 0));


	public class WebPartAction
	{

		public enum ActionType
		{
			Add,
			Delete,
			Move,
			SetProperties
		}

		[XmlIgnore()]
		public WebPart wp = null;

		[XmlElement(Form = XmlSchemaForm.Unqualified)]
		public string assemblyName = string.Empty;

		[XmlElement(Form = XmlSchemaForm.Unqualified)]
		public string className = string.Empty;

		[XmlElement(Form = XmlSchemaForm.Unqualified)]
		public string zoneID = string.Empty;

		//using a string to greatly simplify potential errors during deserialization
		[XmlElement(Form = XmlSchemaForm.Unqualified)]	
		public string zoneIndex = "0";

		[XmlElement(Form = XmlSchemaForm.Unqualified)]
		public string typeName = string.Empty;

		[XmlElement(Form = XmlSchemaForm.Unqualified)]
		public ActionType Action;

		[XmlElement(Form = XmlSchemaForm.Unqualified)]
		public PropertyRoot Properties;

		public WebPartAction()
		{
			//parameter-less constructor needed for serialization
		}

		public WebPartAction(WebPart wp, ActionType Action)
		{
			this.wp = wp;
			this.Action = Action;
		}

		public WebPartAction(WebPart wp, ActionType Action, string zoneID, string zoneIndex)
		{
			this.wp = wp;
			this.Action = Action;
			this.zoneID = zoneID;
			this.zoneIndex = zoneIndex;
		}

		public class PropertyRoot
		{
			[XmlElement(Form = XmlSchemaForm.Unqualified)]
			public OneProperty[] Property;
		}

		public class OneProperty
		{
			[XmlAttribute(Form = XmlSchemaForm.Unqualified)]
			public string Key = string.Empty;

			[XmlAttribute(Form = XmlSchemaForm.Unqualified)]
			public string Value = string.Empty;
		}

	}
}
